home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / plan / src / main.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  25KB  |  876 lines

  1. /*
  2.  * Initializes everything and starts a calendar window for the current
  3.  * month. The interval timer (for autosave and entry recycling) and a
  4.  * few routines used by everyone are also here.
  5.  *
  6.  *    main(argc, argv)        Guess.
  7.  *    fatal(char *fmt, ...)        Prints an error message and exits.
  8.  *    print_button(w, fmt, ...)    Prints a string into a string Label
  9.  *                    or PushButton.
  10.  *    print_text_button(w, fmt, ...)    Prints a string into a Text button.
  11.  *    print_pixmap(w, n)        Prints a pixmap into a Pixmap label.
  12.  *    set_color(col)            Sets the foreground color to one of
  13.  *                    the predefined colors (COL_*).
  14.  *    resynchronize_daemon()        Called when the database on disk has
  15.  *                    changed; tell the daemon to re-read.
  16.  *
  17.  * Author: thomas@bitrot.in-berlin.de (Thomas Driemeyer)
  18.  */
  19.  
  20. #include <stdio.h>
  21. #include <time.h>
  22. #include <fcntl.h>
  23. #ifndef VARARGS
  24. #include <stdarg.h>
  25. #endif
  26. #ifndef MIPS
  27. #include <unistd.h>
  28. #include <stdlib.h>
  29. #endif
  30. #include <ctype.h>
  31. #include <signal.h>
  32. #include <string.h>
  33. #include <sys/types.h>
  34. #include <Xm/Xm.h>
  35. #include <X11/StringDefs.h>
  36. #ifdef JAPAN
  37. #include <jctype.h>
  38. #include <locale.h>
  39. #endif
  40. #include "cal.h"
  41. #include "version.h"
  42.  
  43. #ifdef JAPAN
  44. #ifndef LOCALE_SJIS
  45. #define LOCALE_SJIS    "ja_JP.SJIS"
  46. #endif
  47. #ifndef LOCALE_EUC
  48. #define LOCALE_EUC    "ja_JP.EUC"
  49. #endif
  50. #endif
  51.  
  52. extern char *mkdatestring(), *mktimestring(), *mknotestring();
  53. extern char *mystrdup(), *parse_holidays();
  54. extern time_t tm_to_time(), parse_datestring();
  55. extern struct tm *time_to_tm();
  56. extern time_t get_time();
  57. extern void set_tzone();
  58. extern BOOL startup_lock(), lookup_entry(), lookup_next_entry();
  59. static void init_resources(), init_colors(), init_fonts(), init_daemon();
  60. static void timer_callback(), usage(), non_interactive(), sighand();
  61. static int  dumptoday();
  62. #ifdef MIPS
  63. void exit();
  64. extern char *getenv();
  65. #endif
  66.  
  67. Display            *display;    /* everybody uses the same server */
  68. GC            jgc, gc;    /* graphic context, Japanese and std */
  69. XtAppContext        app;        /* application handle */
  70. Widget            toplevel;    /* top-level shell for icon name */
  71. struct mainmenu        mainmenu;    /* all important main window widgets */
  72. struct config        config;        /* global configuration data */
  73. char            *progname;    /* argv[0] */
  74. BOOL            interactive;    /* interactive or fast appt entry? */
  75. XFontStruct        *font[NFONTS];    /* fonts: FONT_* */
  76. Pixel            color[NCOLS];    /* colors: COL_* */
  77. int            curr_month;    /* month being displayed, 0..11 */
  78. int            curr_year;    /* year being displayed, since 1900 */
  79. struct list        *mainlist;    /* list of all schedule entries */
  80. extern Pixmap        pixmap[NPICS];    /* common symbols */
  81. static BOOL        nodaemon;    /* don't insist on a daemon (-s) */
  82. static XtIntervalId    timer_id;    /* identifies 60-second timer */
  83. static BOOL        reread;        /* need to re-read database file */
  84. extern char        lockpath[];    /* lockfile path (from lock.c) */
  85.  
  86. #ifdef JAPAN
  87. char            *localename;            /* locale name */
  88. unsigned short        (*kanji2jis)() = euc2jis;    /* or sjis2jis() */
  89. #endif
  90.  
  91. static String fallbacks[] = {
  92. #include "resources.h"
  93. #include "print_res.h"
  94.     NULL
  95. };
  96.  
  97.  
  98. /*
  99.  * initialize everything and create the main calendar window
  100.  */
  101.  
  102. main(argc, argv)
  103.     int            argc;
  104.     char            *argv[];
  105. {
  106.     int            n, i;
  107.     time_t            now;
  108.     struct tm        *tm;
  109.     char            buf[80], *name;
  110.     BOOL            nofork = FALSE;
  111.     BOOL            nolock = FALSE;
  112.     char            *errmsg;
  113.  
  114.     interactive = FALSE;
  115.     if ((progname = strrchr(argv[0], '/')) && progname[1])
  116.         progname++;
  117.     else
  118.         progname = argv[0];
  119.     tzset();
  120.     now = get_time();
  121.     tm = time_to_tm(now);
  122.     curr_month = tm->tm_mon;
  123.     curr_year  = tm->tm_year;
  124.     set_tzone();
  125.  
  126. #ifdef JAPAN
  127.     if (strcmp(localename = setlocale(LC_CTYPE, ""), JAPAN_SJIS)) {
  128.         if(strcmp(localename, JAPAN_EUC))
  129.             fprintf(stderr,
  130.                 "Unknown locale name %s, defaulting to EUC\n",
  131.                     localename);
  132.     } else
  133.         kanji2jis = sjis2jis;
  134. #endif
  135.     if (argc > 1 && isdigit(*argv[1])) {        /* non-interactive? */
  136.         non_interactive(argc, argv);
  137.         _exit(0);
  138.     }
  139.     for (n=1; n < argc; n++)            /* options */
  140.         if (*argv[n] != '-')
  141.             usage();
  142.         else if (argv[n][2] < 'a' || argv[n][2] > 'z')
  143.             switch(argv[n][1]) {
  144.               case 'd':
  145.                 for (i=0; fallbacks[i]; i++)
  146.                     printf("%s%s\n", progname,
  147.                              fallbacks[i]);
  148.                 fflush(stdout);
  149.                 return(0);
  150.               case 'v':
  151.                 fprintf(stderr, "%s: %s %s\n",
  152.                     progname, VERSION, PATCHLEVEL);
  153.                 return(0);
  154.               case 's':
  155.                 nodaemon = TRUE;
  156.                 break;
  157.               case 'f':
  158.                 nofork = TRUE;
  159.                 break;
  160.               case 'k':
  161.                 nolock = TRUE;
  162.                 break;
  163.               case 't':
  164.                 return(dumptoday(argv[n][2] ?
  165.                          argv[n]+2 : argv[n+1]));
  166.               default:
  167.                 usage();
  168.             }
  169.         else
  170.             break;
  171.  
  172.     (void)umask(0077);
  173.     if (!nofork) {                    /* background */
  174.         PID_T pid = fork();
  175.         if (pid < 0)
  176.             perror("can't fork");
  177.         else if (pid > 0)
  178.             _exit(0);
  179.     }
  180.     interactive = TRUE;                /* init interactive */
  181.     config.ewarn_window = TRUE;
  182.     config.lwarn_window = TRUE;
  183.     config.alarm_window = TRUE;
  184.     config.early_time   = 45*60;
  185.     config.late_time    = 5*60;
  186.     name            = getenv("LOGNAME");
  187.     if (!name) name = getenv("USER");
  188.     if (!name) name = getenv("user");
  189.     if (!name) name = "username";
  190.     sprintf(buf, "Mail -s %%s %.65s", name);
  191.     config.mailer = mystrdup(buf);
  192.  
  193.     toplevel = XtAppInitialize(&app, "Plan", NULL, 0,
  194. #        ifndef XlibSpecificationRelease
  195.                 (Cardinal *)&argc, argv,
  196. #        else
  197.                 &argc, argv,
  198. #        endif
  199.                 fallbacks, NULL, 0);
  200. #    ifdef EDITRES
  201.     XtAddEventHandler(toplevel, (EventMask)0, TRUE, 
  202.                 _XEditResCheckMessages, NULL);
  203. #    endif
  204.     display = XtDisplay(toplevel);
  205.     jgc = XCreateGC(display, DefaultRootWindow(display), 0, 0);
  206.     gc  = XCreateGC(display, DefaultRootWindow(display), 0, 0);
  207.  
  208.     init_resources();
  209.     init_colors();
  210.     init_fonts();
  211.     init_pixmaps();
  212.     set_icon(toplevel, 0);
  213.  
  214.     create_list(&mainlist);
  215.     (void)readfile(&mainlist, DB_PUB_PATH,  TRUE);
  216.     (void)readfile(&mainlist, DB_PRIV_PATH, FALSE);
  217.     create_cal_widgets(toplevel);
  218.  
  219.     if (config.autodel)
  220.         (void)recycle_all(mainlist, TRUE, 0);
  221.     signal(SIGHUP, sighand);
  222.                             /* lockfile ok? */
  223.     if (!startup_lock(PLAN_PATH, nolock))
  224.         create_multiple_popup(nolock);
  225.                             /* holidays ok? */
  226.     if (errmsg = parse_holidays(curr_year, TRUE))
  227.         create_error_popup(mainmenu.cal, 0, errmsg);
  228.  
  229.                             /* start */
  230.     timer_id = XtAppAddTimeOut(app, TIMER_PERIOD, timer_callback, 0);
  231.     XtRealizeWidget(toplevel);
  232.     resynchronize_daemon();
  233.     XtAppMainLoop(app);
  234.     return(0);
  235. }
  236.  
  237.  
  238. /*
  239.  * if the first argument is numeric, we are in non-interactive mode. Add a
  240.  * single appointment at the specified time, signal the plan program to
  241.  * re-read the database, and terminate.
  242.  */
  243.  
  244. static void non_interactive(argc, argv)
  245.     int        argc;            /* arguments from main */
  246.     char        *argv[];        /* arguments from main */
  247. {
  248.     time_t        now;
  249.     struct tm    *tm;            /* current date and time */
  250.     struct tm    tm1;            /* for time conversion */
  251.     time_t        trigger;        /* trigger date and time */
  252.     char        msg[1024];        /* note from command line */
  253.     struct entry    entry;            /* appointment to add */
  254.     int        n, i;            /* char and arg counters */
  255.     char        lockfile[80];        /* plan lockfile, for sighup */
  256.     FILE        *fp;            /* for reading lockfile */
  257.     PID_T        pid = 0;        /* interactive plan PID */
  258.  
  259.     n = strlen(argv[1]);
  260.     if (n != 4 && n != 8)
  261.         usage();
  262.     i = atoi(argv[1]);
  263.  
  264.     now = get_time();
  265.     tm = time_to_tm(now);
  266.     tm1.tm_sec   = 0;
  267.     tm1.tm_min   = i % 100;
  268.     tm1.tm_hour  = (i/100) % 100;
  269.     tm1.tm_mday  = n == 8 ? (i/10000)%100     : tm->tm_mday;
  270.     tm1.tm_mon   = n == 8 ? (i/1000000)%100-1 : tm->tm_mon;
  271.     tm1.tm_year  = curr_year + (tm1.tm_mon < tm->tm_mon);
  272.     trigger = tm_to_time(&tm1);
  273.     for (i=2; i < argc; i++) {
  274.         if (strlen(msg) + strlen(argv[i]) > 1021)
  275.             break;
  276.         strcat(msg, argv[i]);
  277.         if (i < argc-1)
  278.             strcat(msg, " ");
  279.     }
  280.     entry.message       = 0;
  281.     entry.script       = 0;
  282.     entry.meeting       = 0;
  283.     entry.note       = msg;
  284.     entry.time       = trigger;
  285.     entry.length       = 0;
  286.     entry.early_warn   = 0;
  287.     entry.late_warn       = 0;
  288.     entry.rep_every       = 0;
  289.     entry.rep_last       = 0;
  290.     entry.rep_weekdays = 0;
  291.     entry.rep_days       = 0;
  292.     entry.rep_yearly   = 0;
  293.     entry.suspended       = 0;
  294.     entry.private       = 0;
  295.     entry.noalarm       = 0;
  296.     entry.notime       = 0;
  297.     entry.triggered       = 0;
  298.  
  299.     create_list(&mainlist);
  300.     (void)readfile(&mainlist, DB_PUB_PATH,  TRUE);
  301.     (void)readfile(&mainlist, DB_PRIV_PATH, FALSE);
  302.     (void)add_entry(&mainlist, &entry);
  303.     write_database(mainlist, DB_PUB_PATH, WR_PUBLIC | WR_CONFIG);
  304.     write_database(mainlist, DB_PRIV_PATH, WR_PRIVATE);
  305.     resynchronize_daemon();
  306.     printf("%s %s \"%.57s\"\n", mkdatestring(trigger),
  307.                     mktimestring(trigger, FALSE), msg);
  308.  
  309.     sprintf(lockfile, PLAN_PATH, (int)getuid());
  310.     if (fp = fopen(lockfile, "r")) {
  311.         fscanf(fp, "%d", &pid);
  312.         if (pid > 1)
  313.             (void)kill(pid, SIGHUP);
  314.     }
  315. }
  316.  
  317.  
  318. /*
  319.  * -t option, prints an ascii summary of all appointments on a day. This
  320.  * is useful for cronjobs that send mail every morning. Return 1 if there
  321.  * are no entries; this will be used as exit code (to suppress mail).
  322.  */
  323.  
  324. static int dumptoday(date)
  325.     char        *date;        /* dump what day, "" is today */
  326. {
  327.     struct lookup    lookup;        /* for finding entries */
  328.     time_t        start;        /* 0:00 of the day to dump */
  329.     BOOL        found;        /* TRUE if there is another entry */
  330.     int        numfound = 0;    /* # of entries dumped */
  331.     char        timestr[20];    /* buffer for time string */
  332.  
  333.     create_list(&mainlist);
  334.     (void)readfile(&mainlist, DB_PUB_PATH,  TRUE);
  335.     (void)readfile(&mainlist, DB_PRIV_PATH, FALSE);
  336.     rebuild_repeat_chain(mainlist);
  337.     start = parse_datestring(date && *date ? date : "today", 0);
  338.     found = lookup_entry(&lookup, mainlist, start, FALSE, FALSE);
  339.     while (found && lookup.trigger < start + 86400) {
  340.         struct entry *ep = &mainlist->entry[lookup.index];
  341.         if (!ep->suspended) {
  342.             if (!numfound++)
  343.                 printf("%s:\n", mkdatestring(start));
  344.             strcpy(timestr, mktimestring(lookup.trigger, FALSE));
  345.             printf("%-6s  %5s   %-63s\n",
  346.                     timestr,
  347.                     mktimestring(ep->length, TRUE),
  348.                     mknotestring(ep));
  349.         }
  350.         found = lookup_next_entry(&lookup);
  351.     }
  352.     if (!numfound)
  353.         printf("no appointments on %s\n", mkdatestring(start));
  354.     return(numfound == 0);
  355. }
  356.  
  357.  
  358. /*
  359.  * signal handler. SIGHUP re-reads the database. It is sent when another
  360.  * plan program is started in non-interactive mode, by specifying an
  361.  * appointment on the command line. That entry should appear in our menus.
  362.  */
  363.  
  364. static void sighand(sig)
  365.     int            sig;        /* signal type */
  366. {
  367.     if (sig == SIGHUP) {                /* re-read database */
  368.         signal(SIGHUP, sighand);
  369.         reread = TRUE;
  370.     } else {                    /* die */
  371.         (void)unlink(lockpath);
  372.         fprintf(stderr, "%s: killed with signal %d\n", progname, sig);
  373.         exit(0);
  374.     }
  375. }
  376.  
  377.  
  378. /*
  379.  * whenever something goes seriously wrong, this routine is called. It makes
  380.  * code easier to read. fatal() never returns. This may fail horribly if
  381.  * VARARGS is defined, but at least it's going to do *something*.
  382.  */
  383.  
  384. #ifndef VARARGS
  385. /*VARARGS*/
  386. fatal(char *fmt, ...)
  387. {
  388.     va_list            parm;
  389.  
  390.     va_start(parm, fmt);
  391.     fprintf(stderr, "%s: ", progname);
  392.     vfprintf(stderr, fmt, parm);
  393.     va_end(parm);
  394.     putc('\n', stderr);
  395.     exit(1);
  396. }
  397.  
  398. #else
  399. /*VARARGS*/
  400. fatal(fmt, a, b, c, d)
  401.     char    *fmt;
  402.     int    a, b, c, d;
  403. {
  404.     fprintf(stderr, fmt, a, b, c, d);
  405.     putc('\n', stderr);
  406.     exit(1);
  407. }
  408. #endif
  409.  
  410.  
  411. /*
  412.  * Ultrix doesn't have strdup, so we'll need to define one locally.
  413.  */
  414.  
  415. char *mystrdup(s)
  416.     register char *s;
  417. {
  418.     register char *p = NULL;
  419.  
  420.     if (s && (p = (char *)malloc(strlen(s)+1)))
  421.         strcpy(p, s);
  422.     return(p);
  423. }
  424.  
  425.  
  426. /*
  427.  * exit is redefined here so it the database is written back before the
  428.  * program dies for any reason - including Motif-Quit.
  429.  * How is this for a hack :-) That's what you get for downloading sources
  430.  * from alt.sources...
  431.  */
  432.  
  433. void exit(ret)
  434. {
  435.     static int exiting = 0;
  436.     if (!exiting++) {
  437.         if (*lockpath)
  438.             (void)unlink(lockpath);
  439.         if (mainlist && mainlist->modified) {
  440.             write_database(mainlist, DB_PUB_PATH,    WR_PUBLIC |
  441.                                 WR_CONFIG);
  442.             write_database(mainlist, DB_PRIV_PATH,    WR_PRIVATE);
  443.             resynchronize_daemon();
  444.         }
  445.     }
  446.     _exit(ret);
  447. }
  448.  
  449.  
  450. /*
  451.  * usage information
  452.  */
  453.  
  454. static void usage()
  455. {
  456.     fprintf(stderr, "Usage 1: %s [options]\nOptions:\n%s%s%s%s%s%s%s%s\n",
  457.             progname,
  458.             "\t-h\tprint this help text\n",
  459.             "\t-d\tdump fallback app-defaults and exit\n",
  460.             "\t-v\tprint version string\n",
  461.             "\t-t D\ttext dump for date D, default is today\n",
  462.             "\t\teg: -t, -t +3, -t tomorrow, -t 24.12., -t wed\n",
  463.             "\t-s\tstandalone, don't offer to start daemon\n",
  464.             "\t-f\tdon't fork on startup\n",
  465.             "\t-k\tignore lock file and start up in any case\n");
  466.     fprintf(stderr, "Usage 2: %s [mmdd]hhmm [message]*\n%s\n", progname,
  467.             "\tAdd appointment at mm/dd hh:mm (default is today)");
  468.     exit(1);
  469. }
  470.  
  471.  
  472. /*---------------------------------------------------------------------------*/
  473. /*
  474.  * draw some text into a button. This is here because it's used by many
  475.  * routines. There are two versions, stdarg and varargs, because Sun has
  476.  * the gall to ship a pre-ansi compiler unless you pay extra...
  477.  */
  478.  
  479. #ifndef VARARGS
  480. print_button(Widget w, char *fmt, ...)
  481. {
  482.     va_list            parm;
  483.     Arg            args;
  484.     XmString        string;
  485.     char            buf[1024];
  486.  
  487.     if (fmt && w) {
  488.         va_start(parm, fmt);
  489.         vsprintf(buf, fmt, parm);
  490.         va_end(parm);
  491.         string = XmStringCreateSimple(*buf ? buf : " ");
  492.         XtSetArg(args, XmNlabelString, string);
  493.         XtSetValues(w, &args, 1);
  494.         XmStringFree(string);
  495.     }
  496. }
  497.  
  498. print_text_button(Widget w, char *fmt, ...)
  499. {
  500.     va_list            parm;
  501.     char            buf[1024];
  502.  
  503.     if (w) {
  504.         va_start(parm, fmt);
  505.         vsprintf(buf, fmt, parm);
  506.         va_end(parm);
  507.         XmTextSetString(w, *buf ? buf : " ");
  508.         XmTextSetInsertionPosition(w, strlen(buf));
  509.     }
  510. }
  511.  
  512. #else /* VARARGS */
  513.  
  514. print_button(w, fmt, a, b, c, d)
  515.     Widget            w;
  516.     char            *fmt;
  517.     int            a, b, c, d;
  518. {
  519.     Arg            args;
  520.     XmString        string;
  521.     char            buf[1024];
  522.  
  523.     if (fmt && w) {
  524.         sprintf(buf, fmt, a, b, c, d);
  525.         string = XmStringCreateSimple(buf);
  526.         XtSetArg(args, XmNlabelString, string);
  527.         XtSetValues(w, &args, 1);
  528.         XmStringFree(string);
  529.     }
  530. }
  531.  
  532. print_text_button(w, fmt, a, b, c, d)
  533.     Widget            w;
  534.     char            *fmt;
  535.     int            a, b, c, d;
  536. {
  537.     XmString        string;
  538.     char            buf[1024];
  539.  
  540.     if (w) {
  541.         sprintf(buf, fmt, a, b, c, d);
  542.         string = XmStringCreateSimple(buf);
  543.         XmTextSetString(w, *buf ? buf : " ");
  544.         XmTextSetInsertionPosition(w, strlen(buf));
  545.         XmStringFree(string);
  546.     }
  547. }
  548. #endif /* VARARGS */
  549.  
  550.  
  551.  
  552. /*
  553.  * draw a pixmap into a button, or remove the pixmap. The button must have
  554.  * been a pixmap button all along, this routine can't mix text and pixmaps.
  555.  * If <n> is -1, draw a blank pixmap.
  556.  */
  557.  
  558. print_pixmap(w, n)
  559.     Widget            w;        /* button to draw into */
  560.     int            n;        /* pixmap #, one of PIC_* */
  561. {
  562.     Arg            args;
  563.  
  564.     if (w) {
  565.         XtSetArg(args, XmNlabelPixmap, pixmap[n >= 0 ? n : PIC_BLANK]);
  566.         XtSetValues(w, &args, 1);
  567.     }
  568. }
  569.  
  570.  
  571. /*
  572.  * read resources and put them into the config struct. This routine is used
  573.  * for getting three types of resources: res_type={XtRInt,XtRBool,XtRString}.
  574.  */
  575.  
  576. void get_rsrc(ret, res_name, res_class_name, res_type)
  577.     XtPointer    ret;
  578.     char        *res_name;
  579.     char        *res_class_name;
  580.     char        *res_type;
  581. {
  582.     XtResource    res_list[1];
  583.  
  584.     res_list->resource_name      = res_name;
  585.     res_list->resource_class  = res_class_name;
  586.     res_list->resource_type      = res_type;
  587.     res_list->resource_size      = sizeof(res_type);
  588.     res_list->resource_offset = 0;
  589.     res_list->default_type      = res_type;
  590.     res_list->default_addr      = 0;
  591.  
  592.     XtGetApplicationResources(toplevel, ret, res_list, 1, NULL, 0);
  593. }
  594.  
  595.  
  596. static void init_resources()
  597. {
  598.     register struct config    *c = &config;
  599.  
  600.     get_rsrc(&c->calbox_xs[0],   "calBoxWidth",    "CalBoxWidth",  XtRInt);
  601.     get_rsrc(&c->calbox_ys[0],   "calBoxHeight",   "CalBoxHeight", XtRInt);
  602.     get_rsrc(&c->calbox_marg[0], "calBoxMargin",   "CalBoxMargin", XtRInt);
  603.     get_rsrc(&c->calbox_arrow[0],"calArrowWidth",  "CalArrowWidth",XtRInt);
  604.     get_rsrc(&c->calbox_xs[1],   "calBoxWidthSm",  "CalBoxWidth",  XtRInt);
  605.     get_rsrc(&c->calbox_ys[1],   "calBoxHeightSm", "CalBoxHeight", XtRInt);
  606.     get_rsrc(&c->calbox_marg[1], "calBoxMarginSm", "CalBoxMargin", XtRInt);
  607.     get_rsrc(&c->calbox_arrow[1],"calArrowWidthSm","CalArrowWidth",XtRInt);
  608.     get_rsrc(&c->year_margin,    "yearMargin",     "YearMargin",   XtRInt);
  609.     get_rsrc(&c->year_gap,         "yearGap",        "YearGap",      XtRInt);
  610.     get_rsrc(&c->year_title,     "yearTitle",      "YearTitle",    XtRInt);
  611.     get_rsrc(&c->yearbox_xs,     "yearBoxWidth",   "YearBoxWidth", XtRInt);
  612.     get_rsrc(&c->yearbox_ys,     "yearBoxHeight",  "YearBoxHeight",XtRInt);
  613.     get_rsrc(&c->week_margin,    "weekMargin",     "WeekMargin",   XtRInt);
  614.     get_rsrc(&c->week_gap,         "weekGap",        "WeekGap",      XtRInt);
  615.     get_rsrc(&c->week_daywidth,  "weekDayWidth",   "WeekDayWidth", XtRInt);
  616.     get_rsrc(&c->week_hourwidth, "weekHourWidth",  "WeekHourWidth",XtRInt);
  617.     get_rsrc(&c->week_barheight, "weekBarHeight",  "WeekBarHeight",XtRInt);
  618.     get_rsrc(&c->week_bargap,    "weekBarGap",     "WeekBarGap",   XtRInt);
  619.     get_rsrc(&c->week_maxnote,   "weekMaxNote",    "WeekMaxNote",  XtRInt);
  620.     get_rsrc(&c->showicontime,   "showIconTime",   "ShowIconTime",XtRBool);
  621.     get_rsrc(&c->frame_today,    "frameToday",     "FrameToday",  XtRBool);
  622.     get_rsrc(&c->noicon,         "noIcon",         "NoIcon",      XtRBool);
  623.     get_rsrc(&c->sgimode,        "sgiMode",        "SgiMode",     XtRBool);
  624. }
  625.  
  626.  
  627. /*
  628.  * determine all colors, and allocate them. They can then be used by a call
  629.  * to set_color(COL_XXX).
  630.  */
  631.  
  632. static void init_colors()
  633. {
  634.     Screen            *screen = DefaultScreenOfDisplay(display);
  635.     Colormap        cmap;
  636.     XColor            rgb;
  637.     int            i, d;
  638.     char            *c, *n, class_name[256];
  639.  
  640.     cmap = DefaultColormap(display, DefaultScreen(display));
  641.     for (i=0; i < NCOLS; i++) {
  642.         switch (i) {
  643.           default:
  644.           case COL_STD:        n = "colStd";        d=1;    break;
  645.           case COL_BACK:    n = "colBack";        d=0;    break;
  646.           case COL_CALBACK:    n = "colCalBack";    d=0;    break;
  647.           case COL_CALSHADE:    n = "colCalShade";    d=0;    break;
  648.           case COL_CALACT:    n = "colCalAct";    d=0;    break;
  649.           case COL_CALTODAY:    n = "colCalToday";    d=0;    break;
  650.           case COL_CALFRAME:    n = "colCalFrame";    d=0;    break;
  651.           case COL_GRID:    n = "colGrid";        d=1;    break;
  652.           case COL_WEEKDAY:    n = "colWeekday";    d=1;    break;
  653.           case COL_WEEKEND:    n = "colWeekend";    d=1;    break;
  654.           case COL_NOTE:    n = "colNote";        d=1;    break;
  655.           case COL_NOTEOFF:    n = "colNoteOff";    d=1;    break;
  656.           case COL_TOGGLE:    n = "colToggle";    d=1;    break;
  657.           case COL_RED:        n = "colRed";        d=1;    break;
  658.           case COL_TEXTBACK:    n = "colTextBack";    d=0;    break;
  659.           case COL_YBACK:    n = "colYearBack";    d=0;    break;
  660.           case COL_YBOXBACK:    n = "colYearBoxBack";    d=0;    break;
  661.           case COL_YNUMBER:    n = "colYearNumber";    d=1;    break;
  662.           case COL_YWEEKDAY:    n = "colYearWeekday";    d=1;    break;
  663.           case COL_YMONTH:    n = "colYearMonth";    d=1;    break;
  664.           case COL_YTITLE:    n = "colYearTitle";    d=1;    break;
  665.           case COL_YGRID:    n = "colYearGrid";    d=1;    break;
  666.           case COL_HBLACK:    n = "colHolidayBlack";    d=1;    break;
  667.           case COL_HRED:    n = "colHolidayRed";    d=1;    break;
  668.           case COL_HGREEN:    n = "colHolidayGreen";    d=1;    break;
  669.           case COL_HYELLOW:    n = "colHolidayYellow";    d=1;    break;
  670.           case COL_HBLUE:    n = "colHolidayBlue";    d=1;    break;
  671.           case COL_HMAGENTA:    n = "colHolidayMagenta";d=1;    break;
  672.           case COL_HCYAN:    n = "colHolidayCyan";    d=1;    break;
  673.           case COL_HWHITE:    n = "colHolidayWhite";    d=0;    break;
  674.           case COL_WBACK:    n = "colWeekBack";    d=0;    break;
  675.           case COL_WBOXBACK:    n = "colWeekBoxback";    d=0;    break;
  676.           case COL_WTITLE:    n = "colWeekTitle";    d=1;    break;
  677.           case COL_WGRID:    n = "colWeekGrid";    d=1;    break;
  678.           case COL_WDAY:    n = "colWeekDay";    d=1;    break;
  679.           case COL_WNOTE:    n = "colWeekNote";    d=1;    break;
  680.           case COL_WFRAME:    n = "colWeekFrame";    d=1;    break;
  681.           case COL_WWARN:    n = "colWeekWarn";    d=0;    break;
  682.           case COL_WUSER_0:    n = "colWeekUser_0";    d=0;    break;
  683.           case COL_WUSER_1:    n = "colWeekUser_1";    d=0;    break;
  684.           case COL_WUSER_2:    n = "colWeekUser_2";    d=0;    break;
  685.           case COL_WUSER_3:    n = "colWeekUser_3";    d=0;    break;
  686.           case COL_WUSER_4:    n = "colWeekUser_4";    d=0;    break;
  687.           case COL_WUSER_5:    n = "colWeekUser_5";    d=0;    break;
  688.           case COL_WUSER_6:    n = "colWeekUser_6";    d=0;    break;
  689.           case COL_WUSER_7:    n = "colWeekUser_7";    d=0;    break;
  690.         }
  691.         strcpy(class_name, n);
  692.         class_name[0] &= ~('a'^'A');
  693.         get_rsrc(&c, n, class_name, XtRString);
  694.         if (!XParseColor(display, cmap, c, &rgb))
  695.             fprintf(stderr, "%s: unknown color \"%s\" (%s)\n",
  696.                             progname, c, n);
  697.         else if (!XAllocColor(display, cmap, &rgb))
  698.             fprintf(stderr, "%s: can't alloc color \"%s\" (%s)\n",
  699.                             progname, c, n);
  700.         else {
  701.             color[i] = rgb.pixel;
  702.             continue;
  703.         }
  704.         color[i] = d ? BlackPixelOfScreen(screen)
  705.                  : WhitePixelOfScreen(screen);
  706.     }
  707. }
  708.  
  709.  
  710. set_color(col)
  711.     int            col;
  712. {
  713.     XSetForeground(display, jgc, color[col]);
  714.     XSetForeground(display, gc,  color[col]);
  715. }
  716.  
  717.  
  718. /*
  719.  * load all fonts and make them available in the "fonts" struct. They are
  720.  * loaded into the GC as necessary.
  721.  */
  722.  
  723. static void init_fonts()
  724. {
  725.     int            i;
  726.     char            *f, class_name[256];
  727.  
  728.     for (i=0; i < NFONTS; i++) {
  729.         switch (i) {
  730.           default:
  731. #ifdef JAPAN
  732.           case FONT_STD:    f = "stdFont";        break;
  733.           case FONT_JNOTE:    f = "jNoteFont";    break;
  734. #else
  735.             case FONT_STD:    f = "fontList";        break;
  736. #endif
  737.           case FONT_HELP:    f = "helpFont";        break;
  738.           case FONT_DAY:    f = "calNumberFont";    break;
  739.           case FONT_SMDAY:    f = "calNumberFontSm";    break;
  740.           case FONT_NOTE:    f = "calNoteFont";    break;
  741.           case FONT_YTITLE:    f = "yearTitleFont";    break;
  742.           case FONT_YMONTH:    f = "yearMonthFont";    break;
  743.           case FONT_YDAY:    f = "yearWeekdayFont";    break;
  744.           case FONT_YNUM:    f = "yearNumberFont";    break;
  745.           case FONT_WTITLE:    f = "weekTitleFont";    break;
  746.           case FONT_WDAY:    f = "weekDayFont";    break;
  747.           case FONT_WHOUR:    f = "weekHourFont";    break;
  748.           case FONT_WNOTE:    f = "weekNoteFont";    break;
  749.         }
  750.         strcpy(class_name, f);
  751.         class_name[0] &= ~('a'^'A');
  752.         get_rsrc(&f, f, class_name, XtRString);
  753.         if (!(font[i] = XLoadQueryFont(display, f))) {
  754.             fprintf(stderr, "plan: warning: bad font \"%s\"\n", f);
  755.             if (!(font[i] = XLoadQueryFont(display, "variable")) &&
  756.                 !(font[i] = XLoadQueryFont(display, "fixed")))
  757.                 fatal("can't load font \"variable\"\n", f);
  758.         }
  759.     }
  760.     config.calbox_title[0]    = font[FONT_STD]   ->max_bounds.ascent +
  761.                   font[FONT_STD]   ->max_bounds.descent;
  762.     config.calbox_title[1]    = font[FONT_SMDAY] ->max_bounds.ascent +
  763.                   font[FONT_SMDAY] ->max_bounds.descent;
  764.     config.week_title    = font[FONT_WTITLE]->max_bounds.ascent +
  765.                   font[FONT_WTITLE]->max_bounds.descent;
  766.     config.week_hour    = font[FONT_WHOUR] ->max_bounds.ascent +
  767.                   font[FONT_WHOUR] ->max_bounds.descent + 6;
  768. }
  769.  
  770.  
  771. /*
  772.  * get the process ID of the daemon. This pid gets a SIGHUP signal whenever
  773.  * the database has been written back to disk. The daemon then re-reads it.
  774.  * If there is no daemon, set the PID to 0.
  775.  */
  776.  
  777. static PID_T        daemon_pid;    /* process ID of daemon (gets SIGHUP)*/
  778.  
  779. static void init_daemon()
  780. {
  781.     int            lockfd;        /* lockfile descriptor */
  782.     char            path[256];    /* lockfile path */
  783.     char            buf[12];    /* contents of file (pid) */
  784.  
  785.     daemon_pid = 0;
  786.     sprintf(path, LOCK_PATH, (int)getuid());
  787.     if ((lockfd = open(path, O_RDONLY)) < 0) {
  788.         if (!nodaemon) {
  789.             fprintf(stderr, "%s: WARNING: no daemon: ",progname);
  790.             perror("");
  791.         }
  792.         return;
  793.     }
  794.     if (read(lockfd, buf, 10) < 5) {
  795.         fprintf(stderr,"%s: WARNING: daemon lockfile %s unreadable: ",
  796.                             progname, path);
  797.         perror("");
  798.         return;
  799.     }
  800.     close(lockfd);
  801.     buf[10] = 0;
  802.     daemon_pid = atoi(buf);
  803. }
  804.  
  805. resynchronize_daemon()
  806. {
  807.     if (!daemon_pid || kill(daemon_pid, SIGHUP)) {
  808.         init_daemon();
  809.         if (!nodaemon && (!daemon_pid || kill(daemon_pid, SIGHUP))) {
  810.             fprintf(stderr, "%s: WARNING: can't signal daemon: ",
  811.                                 progname);
  812.             perror("");
  813.             if (interactive)
  814.                 create_nodaemon_popup();
  815.             else
  816.                 fprintf(stderr,
  817.                        "%s: WARNING: no daemon, run pland\n",
  818.                                 progname);
  819.         }
  820.     }
  821. }
  822.  
  823.  
  824. /*
  825.  * this routine gets called every 10 seconds. Redraw the main calendar when
  826.  * midnight passes, to move the green highlight. Write the main list if it
  827.  * has changed. Also, see if any old entries need to be recycled or deleted.
  828.  * If we got a SIGHUP signal, re-read the database when it is safe.
  829.  */
  830.  
  831. /*ARGSUSED*/
  832. static void timer_callback(data, id)
  833.     XtPointer        data;        /* not used */
  834.     XtIntervalId        *id;        /* not used */
  835. {
  836.     static time_t        last_tod;    /* previous time-of-day */
  837.     time_t            tod;        /* current time-of-day */
  838.  
  839.     timer_id = XtAppAddTimeOut(app, TIMER_PERIOD, timer_callback, 0);
  840.     tod = get_time();
  841.     set_tzone();
  842.     tod %= 86400;
  843.     if (tod < last_tod) {
  844.         draw_calendar();
  845.         draw_week_calendar();
  846.     }
  847.     if (tod/60 != last_tod/60) {
  848.         print_icon_name();
  849.         if (config.smallmonth)
  850.             print_button(mainmenu.time,
  851.                     mktimestring(get_time(), FALSE));
  852.         else
  853.             print_button(mainmenu.time, "%s   %s",
  854.                     mkdatestring(get_time(), FALSE),
  855.                     mktimestring(get_time(), FALSE));
  856.     }
  857.     last_tod = tod;
  858.     if (mainlist->modified && !mainlist->locked) {
  859.         write_database(mainlist, DB_PUB_PATH,  WR_PUBLIC | WR_CONFIG);
  860.         write_database(mainlist, DB_PRIV_PATH, WR_PRIVATE);
  861.         resynchronize_daemon();
  862.     }
  863.     if (!mainlist->modified && !mainlist->locked && reread) {
  864.         reread = FALSE;
  865.         destroy_list(&mainlist);
  866.         (void)readfile(&mainlist, DB_PUB_PATH,  TRUE);
  867.         (void)readfile(&mainlist, DB_PRIV_PATH, FALSE);
  868.         draw_calendar();
  869.         update_all_listmenus();
  870.     }
  871.     if (config.autodel && recycle_all(mainlist, TRUE, 0)) {
  872.         draw_calendar();
  873.         update_all_listmenus();
  874.     }
  875. }
  876.